查看原文
其他

【进阶】除数为0,程序会奔溃吗?

bug菌 最后一个bug 2021-01-31


1、聊一聊

    今天这里分享了一首杰伦的老歌,每次听这首歌都会有一种初恋的感觉,其实生活也是一样的简简单单就好!

    好了,今天为大伙带来的小知识不算太难,不过应该也是大家在平时的编程实践中容易忽略的一些点,为了编写出更加健壮的代码,所以作者带大家好好了解一下!

2、整形遇上除数0

    在大部分小伙伴的观念里面一个非0数除以0应该都会等于±∞,这是我们潜意思里面成立的,毕竟初中数学老师都是这么教的,其实我们的编程就是一种数学计算,那大家有没有想过计算机或者单片机对于无穷是如何表示的?或者说一个非0除以0会得到什么结果?那么看看下面实际的操作。

1)在DevC++里面 

写个测试demo:
1#include <stdio.h>
2#include <stdlib.h>
3
4int main(int argc, char *argv[]) {
5    int  a = 10;
6    int  b = 0;
7    printf("%d\n",a/b); 
8    printf("公众号:最后一个bug\n"); 
9    return 0;
10}
运行结果:

    在我们PC机上用dev运行除数为0的整形运算是直接报错的,因为这里变量为0作为除数,如果是常量0作为除数一般都会是提示编译告警或者直接错误。可能大部分小伙伴觉得单片机功能简单应该做不了这么智能化,那我们在单片机里面运行一下看会不会引起什么异常?

2)在stm32中测试 

写个测试demo:
1#include "led.h"
2#include "delay.h"
3#include "sys.h"
4
5int a = 1000;
6int b = 0;
7int c = 0;
8
9int main(void)
10
{    
11    delay_init();       
12    LED_Init();         
13    uart_init(115200);
14    printf("--->欢迎关注公众号:最后一个bug\r\n");
15
16    c = a/b;
17
18    if(c > 0)
19    {
20        printf("c > 0\r\n");
21    }
22    else
23    {
24        printf("c <= 0\r\n");
25    }
26
27    printf("c = %d\r\n",c);
28
29    while(1){;}
30}
输出结果:

    stm32单片机输出竟然是0,1000/0也最终不会大于0,这样与我们程序的运行思路是不符合的,所以经常有小伙伴有这样的案例:通过AD采样获得整形值,然后用该值作为除数进行算数变换,最终进行相应判断可能就会出问题。

    从上面两个例子可以看出来不同平台对于整形除数0的处理机制都有所不同,并且有些平台对于该除0操作会触发对应的异常中断等,作为一个异常交给用户进行最后的处理,需要根据具体平台具体分析。不过我们在平时的编程中尽量考虑该问题,然后对其进行判断规避掉分母为0的情况发生。


3、浮点遇上除数0

    既然整形除0这么危险,那不同的浮点数除0会怎样处理呢?我们都知道整形数据溢出以后会形成环形计算<【重磅】“整形数”还真没那么简单(C语言版)>,也可以说是取模计算,而浮点数我们之前倒没有说其溢出问题,难道它也是环形?下面具体看看。

1)在DevC++和stm32上的运行结果 

写个测试demo:
1#include <stdio.h>
2#include <stdlib.h>
3
4int main(int argc, char *argv[]) {
5    float  a = 0;
6    float  b = 1.0;
7    float  result1 = 0;
8    float  result2 = 0;
9    float  result3 = 0;
10    int *pHex = NULL;
11
12    //1)测试 1/0 
13    result1 = b/a;
14    pHex =(int *)&result1;
15    printf("--- 1/0 = %f = 0x%X\n",result1,*pHex);
16
17    //2)测试 -1/0 
18    result2 =-b/a;
19    pHex =(int *)&result2;
20    printf("--- -1/0 = %f = 0x%X\n",result2,*pHex);
21
22    //3)测试 0/0 
23    result3 =a/a;
24    pHex =(int *)&result3;
25    printf("--- 0/0 = %f = 0x%X\n",result3,*pHex);
26
27    printf("公众号:最后一个bug\n"); 
28    return 0;
29}
输出结果如下:

    结果似乎并不是0,不是说好的printf输出的是浮点数吗?怎么输出结果变成了英文?然而打印的16进制数据好像是符合浮点规则的,具体大家可以先看看<【典藏】别怪"浮点数"太坑(C语言版本)>对上面的数据推一推,作者还想给大家做个单片机上的实验,然后再好好讲讲为什么会输出这样的形式?好了,代码我就不板述了,直接看串口输出。

stm32上最终输出:

    DevC++中输出的结果与stm32输出的结果还稍有差异,不过整体上差不多,唯一是最后的nan其Hex格式中的符号位有点不同,毕竟两个平台所遵循的标准有所差异。

2)浮点中±INF和NAN (IEEE 754)

  • 1)首先我们对这几个浮点怪异的输出介绍一下:INF是infinity的缩写表示"无穷大",而NAN是not a Number的缩写,表示“不是一个数字”。

  • 2)其实三者都是对浮点计算结果不确定或者无法表示的计算,并且这里所谓的无穷大是与我们平时数学意义上±∞意义上几乎是一致的,比如任何数+INF = INF;1/INF = 0等等。

  • 3)NAN是比较特殊的形式,其不大于、不小于、不等于任何数字,包括其自身。所以我们经常使用if(x == x)是否为false来判断其是否为NAN;并且几乎NAN参与的任何运算结果基本上都是NAN。

  • 4)从我们之前程序打印的Hex我们大体可以了解到±INF和NAN的浮点表示:

  • +INF
    符号位为0 + 阶码全为1 + 尾码为0
    -INF
    符号位为1 + 阶码全为1 + 尾码为0
    NAN
    符号位为1/0 + 阶码全为1 尾码非0
  • 5)既然在运算过程中有存在这样的不确定的数据,如果我们不是刻意设计,就表示该计算过程有错误的计算出现,所以我们需要通过判断具体的变量是否等于上面的值来进行故障捕捉从而定位问题,一方面我们的可以通过INF和NAN的性质来进行自己封装函数来进行判断,另一方面编译器会存在对应的接口函数或者是宏定义来获得其判定结果。

  • 如下面调用math.h判断函数isinfisnan的简单代码:

1#include <stdio.h>
2#include <stdlib.h>
3#include <math.h>
4
5int main(int argc, char *argv[]) {
6    float  a = 0;
7    float  b = 1.0;
8    float  result1 = 0;
9    float  result2 = 0;
10    float  result3 = 0;
11    int *pHex = NULL;
12
13    //1)测试 1/0 
14    result1 = b/a;
15    pHex =(int *)&result1;
16    printf("--- 1/0 = %f = 0x%X\n",result1,*pHex);
17
18    //2)测试 -1/0 
19    result2 =-b/a;
20    pHex =(int *)&result2;
21    printf("--- -1/0 = %f = 0x%X\n",result2,*pHex);
22
23    //3)测试 0/0 
24    result3 =a/a;
25    pHex =(int *)&result3;
26    printf("--- 0/0 = %f = 0x%X\n",result3 + 5,*pHex);
27
28    //4)通过调用库函数接口判断不确定浮点 
29    printf("isinf(result1) = %d\n",isinf(result1));
30    printf("isinf(result2) = %d\n",isinf(result2));
31    printf("isnan(result3) = %d\n",isnan(result3));
32
33    printf("公众号:最后一个bug\n"); 
34    return 0;
35}

其输出结果:

4、最后小节

    虽然该部分只是C语言的冰山一角,也可以说大部分小伙伴在实际的编程中根本就不会关注,不过一旦遇到几乎有1半的小伙伴摸不着头脑,所以作者这里把一些知识点好好跟大家聊聊,以便大家以后遇到不会觉得陌,该部分和平台的关联比较大,大家可以在自己的目标机器上进行实验并获得相应的结果,同时大家也可以进一步丰富对平台的熟悉度<【经典】"有格调"的MCU初始化(绝对要get)>。

    好了,这里是公众号:“最后一个bug”,一个为大家打造的技术知识提升基地。同时非常感谢各位小伙伴的支持,我们下期精彩见!

推荐好文  点击蓝色字体即可跳转

【重磅】“整形数”还真没那么简单(C语言版)

 【OS】你知道什么叫 " 超线程 " 吗?

【OS】原来应用是这样访问到底层(系统调用)

【重磅】剖析MCU的IAP升级软件设计(设计思路篇)

☞ 【典藏】别怪"浮点数"太坑(C语言版本)

GUI必备知识之“告别”乱码(浅显易懂)

【典藏】大佬们都在用的结构体进阶小技巧

听说因为代码没"对齐"程序就奔了?(深度剖析)

【典藏】自制小型GUI界面框架(设计思想篇)

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存